<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.9.0">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    
    <meta name="author" content="挥手の骑士">
    
    
    
    
    
    
    <title>跟上Java8 Lambda | 骑士の物语</title>
    <link href="https://yihuishou.gitee.io" rel="prefetch">

    <link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/aos.css">
<link rel="stylesheet" href="/css/style.css">
    <script src="/js/jquery.min.js"></script>
    <script src="/js/bootstrap.min.js"></script>
    <script src="/js/aos.js"></script>
    <script src="/js/highslide/highslide-full.min.js"></script>
    <link rel="stylesheet" href="/js/highslide/highslide.css">
    <style type="text/css">
        @media (max-width: 768px) {
            body {
                background-color: #f0f0f0;
                background: url('/imgs/xsbg.gif');
                background-attachment: fixed;
            }
        }
    </style>
    
    <!--<script type="text/javascript">
      if (document.images) {
        var avatar = new Image();
        avatar.src = '/imgs/avatar.jpg'
        var previews = 'picHD_1.png,picHD_2.png,picHD_3.png,picHD_4.png,picHD_5.png,picHD_6.png,picHD_7.png,picHD_8.png,picHD_9.png,picHD_10.png,picHD_11.png,picHD_12.png,picHD_13.png,picHD_14.png,picHD_15.png,picHD_16.png,picHD_17.png,picHD_18.png,picHD_19.png,picHD_20.png,picHD_21.png,picHD_22.png,picHD_23.png,picHD_24.png,picHD_25.png,picHD_26.png,picHD_27.png,picHD_28.png,picHD_29.png,picHD_30.png,picHD_31.png,picHD_32.png,picHD_33.png,picHD_34.png,picHD_35.png,picHD_36.png,picHD_37.png,picHD_38.png,picHD_39.png,picHD_40.png,picHD_41.png,picHD_42.png'.split(',')
        var previewsPreLoad = []
        for(var i = 0; i < length; i++) {
          previewsPreLoad.push(new Image())
          previewsPreLoad[previewsPreLoad.length - 1].src = '/imgs/preview' + previews[i]
        }
      }
    </script>-->
</head></html>
<body>
    <!-- 背景轮播图功能 -->
    <section class="hidden-xs">
    <ul class="cb-slideshow">
        <li><span>天若</span></li>
        <li><span>有情</span></li>
        <li><span>天亦老</span></li>
        <li><span>我为</span></li>
        <li><span>长者</span></li>
        <li><span>续一秒</span></li>
    </ul>
</section>
    <!-- 欧尼酱功能, 谁用谁知道 -->
    
    <header class="navbar navbar-inverse" id="gal-header">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed"
                    data-toggle="collapse" data-target=".bs-navbar-collapse"
                    aria-expanded="false">
                <span class="fa fa-lg fa-reorder"></span>
            </button>
            <a href="https://yihuishou.gitee.io">
                
                <style>
                    #gal-header .navbar-brand {
                        height: 54px;
                        line-height: 24px;
                        font-size: 28px;
                        opacity: 1;
                        background-color: rgba(0,0,0,0);
                        text-shadow: 0 0 5px #fff,0 0 10px #fff,0 0 15px #fff,0 0 20px #228DFF,0 0 35px #228DFF,0 0 40px #228DFF,0 0 50px #228DFF,0 0 75px #228DFF;
                    }
                </style>
                <!-- 这里使用文字(navbar_text or config.title) -->
                <div class="navbar-brand">骑士の物语</div>
                
            </a>
        </div>
        <div class="collapse navbar-collapse bs-navbar-collapse">
            <ul class="nav navbar-nav" id="menu-gal">
                
                
                <li class="">
                    <a href="/">
                        <i class="fa fa-home"></i>首页
                    </a>
                </li>
                
                
                
                <li class="">
                    <a href="/archives">
                        <i class="fa fa-archive"></i>归档
                    </a>
                </li>
                
                
            </ul>
        </div>
    </div>
</header>
    <div id="gal-body">
        <div class="container">
            <div class="row">
                <div class="col-md-8 gal-right" id="mainstay">
                    
<article class="article well article-body" id="article">
    <div class="breadcrumb">
        <i class="fa fa-home"></i>
        <a href="https://yihuishou.gitee.io">骑士の物语</a>
        >
        <span>跟上Java8 Lambda</span>
    </div>
    <!-- 大型设备详细文章 -->
    <div class="hidden-xs">
        <div class="title-article">
            <h1>
                <a href="/2018/09/03/2921176476/">跟上Java8 Lambda</a>
            </h1>
        </div>
        <div class="tag-article">
            
            <span class="label label-gal">
                <i class="fa fa-tags"></i>
                
                <a href="/tags/Java/">Java</a>
                
            </span>
            
            <span class="label label-gal">
                <i class="fa fa-calendar"></i> 2018-09-03
            </span>
            
        </div>
    </div>
    <!-- 小型设备详细文章 -->
    <div class="visible-xs">
        <center>
            <div class="title-article">
                <h4>
                    <a href="/2018/09/03/2921176476/">跟上Java8 Lambda</a>
                </h4>
            </div>
            <p>
                <i class="fa fa-calendar"></i> 2018-09-03
            </p>
            <p>
                
                <i class="fa fa-tags"></i>
                
                <a href="/tags/Java/">Java</a>
                
                
                
            </p>
        </center>
    </div>
    <div class="content-article">
        <p>使用Lambda表达式，我们可以很简洁地传递代码（通常是匿名函数）。</p>
<p>结构</p>
<p>Lambda表达式主要分为三部分：参数列表，箭头，Lambda 主体</p>
<p>语法</p>
<p>(parameters) -&gt; expression<br>(parameters) -&gt; { statements; }<br>如果表达式只有一行，用第一种，多行用第二种。</p>
<p>Java8中，标注了@FunctionalInterface，表明这个接口将是一个函数式接口，它里面只能有一个抽象方法。</p>
<p>常用的函数式接口</p>
<p>JDK已经为我们提供了很多常用的函数式接口： </p>
<ul>
<li>Predicate：java.util.function.Predicate<t>接口定义了一个名叫test的抽象方法，它接受泛型T对象，并返回一个boolean。在需要表示一个涉及类型T的布尔表达式时可以使用。 </t></li>
<li>Consumer：java.util.function.Consumer<t>定义了一个名叫accept的抽象方法，它接受泛型T的对象，没有返回（void）。如果需要访问类型T的对象，并对其执行某些操作，就可以使用这个接口。 </t></li>
<li>Supplier：java.util.function.Supplier<t>不接受对象，返回一个泛型对象T。在需要new一个对象实例时可以使用。 </t></li>
<li>Function：java.util.function.Function&lt;T, R&gt;接口定义了一个叫作apply的方法，它接受一个泛型T的对象，并返回一个泛型R的对象。如果需要定义一个Lambda，将输入对象的信息映射到输出，就可以使用这个接口。</li>
</ul>
<p>原始类型特化</p>
<p>我们知道，泛型只能绑定到引用类型的对象。因此，在使用泛型绑定基本类型的时候，Java会为我们自动装箱和拆箱，但这是会消耗性能的。<br>如果输入和输出都是基本类型时，Java8为我们提供了新的函数式接口，以避免自动装箱拆箱。</p>
<p>简单列举一部分： </p>
<ul>
<li>Predicate：IntPredicate, LongPredicate, DoublePredicate </li>
<li>Consumer：IntConsumer,LongConsumer, DoubleConsumer </li>
<li>Supplier：BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier </li>
<li>Function：IntFunction<r>,LongToDoubleFunction,ToLongFunction<t></t></r></li>
</ul>
<p>从命名可以轻易看出从什么类型转成什么类型，可以在java.util.function包下查看所有接口。</p>
<p>使用局部变量</p>
<p>在使用lambda时，主体代码块内允许使用的外部变量。但是，不允许改变外部变量。这些变量应该声明为final或者事实上是final的（即之后代码中不会改变）</p>
<p>方法引用</p>
<p>方法引用主要有三类：</p>
<p>指向静态方法的方法引用<br>Lambda: (args) -&gt; ClassName.staticMethod(args)<br>方法引用：ClassName :: staticMethod<br>指向任意类型实例方法的方法引用<br>Lambda: (arg0, rest) -&gt; arg0.instanceMethod(rest)<br>方法引用：ClassName :: instanceMethod(arg0 是 ClassName 类型的)<br>指向现有对象的实例方法的方法引用<br>Lambda: (args) -&gt; expr.instanceMethod(args)<br>方法引用：expr :: intanceMethod<br>除此之外，还有构造函数引用：ClassName :: new<br>比如用Map来将构造函数映射到字符串值：</p>
<pre><code>static Map&lt;String, Function&lt;Integer, Fruit&gt;&gt; map = new HashMap&lt;&gt;();
static {
    map.put(&quot;apple&quot;, Apple::new);
    map.put(&quot;orange&quot;, Orange::new);
    // etc...
}

public static Fruit giveMeFruit(String fruit, Integer weight) {
    return map.get(fruit.toLowerCase()).apply(weight);
}</code></pre><p>复合 Lambda 表达式</p>
<p>Comparator、Predicate和Function等函数式接口都有几个可以用来结Lambda表达式的默认方法。</p>
<p>比较器复合</p>
<p>普通排序comparing()<br>Comparator<apple> c = Comparator.comparing(Apple::getWeight);</apple></p>
<p>逆序reversed()<br>inventory.sort(comparing(Apple::getWeight).reversed()); </p>
<p>比较器链thenComparing()<br>inventory.sort(comparing(Apple::getWeight).reversed()<br>    .thenComparing(Apple::getCountry));</p>
<p>谓词复合</p>
<p>3个方法增强已有的Predicate接口： </p>
<ul>
<li>and：与 </li>
<li>or：或 </li>
<li>negate：非</li>
</ul>
<p>请注意，and和or方法是按照在表达式链中的位置，从左向右确定优先级的。因此，a.or(b).and(c)可以看作(a || b) &amp;&amp; c。</p>
<p>函数复合</p>
<p>Function接口有andThen和compose两个默认方法，它们都会返回Function的一个实例。</p>
<p>举个例子：<br>有2个函数，一个加1，一个乘2</p>
<p>Function&lt;Integer, Integer&gt; f = x -&gt; x + 1; // f(x)=x+1<br>Function&lt;Integer, Integer&gt; g = x -&gt; x * 2; // g(x)=2x</p>
<p>andThen()<br>Function&lt;Integer, Integer&gt; h = f.andThen(g); // g(f(x))<br>int result = h.apply(1); // 4</p>
<p>compose()<br>Function&lt;Integer, Integer&gt; h = f.compose(g); // f(g(x))<br>int result = h.apply(1); // 3</p>
<ol>
<li>什么是λ表达式</li>
</ol>
<p>λ表达式本质上是一个匿名方法。让我们来看下面这个例子：</p>
<pre><code>public int add(int x, int y) {
    return x + y;
}</code></pre><p>转成λ表达式后是这个样子：</p>
<pre><code>(int x, int y) -&gt; x + y;</code></pre><p>参数类型也可以省略，Java编译器会根据上下文推断出来：</p>
<pre><code>(x, y) -&gt; x + y; //返回两数之和</code></pre><p>或者</p>
<pre><code>(x, y) -&gt; { return x + y; } //显式指明返回值</code></pre><p>可见λ表达式有三部分组成：参数列表，箭头（-&gt;），以及一个表达式或语句块。</p>
<p>下面这个例子里的λ表达式没有参数，也没有返回值（相当于一个方法接受0个参数，返回void，其实就是Runnable里run方法的一个实现）：</p>
<pre><code>() -&gt; { System.out.println(&quot;Hello Lambda!&quot;); }</code></pre><p>如果只有一个参数且可以被Java推断出类型，那么参数列表的括号也可以省略：</p>
<pre><code>c -&gt; { return c.size(); }</code></pre><ol start="2">
<li>λ表达式的类型（它是Object吗？）</li>
</ol>
<p>λ表达式可以被当做是一个Object（注意措辞）。λ表达式的类型，叫做“目标类型（target type）”。λ表达式的目标类型是“函数接口（functional interface）”，这是Java8新引入的概念。它的定义是：一个接口，如果只有一个显式声明的抽象方法，那么它就是一个函数接口。一般用@FunctionalInterface标注出来（也可以不标）。举例如下：</p>
<pre><code>@FunctionalInterface
public interface Runnable { void run(); }

public interface Callable&lt;V&gt; { V call() throws Exception; }

public interface ActionListener { void actionPerformed(ActionEvent e); }

public interface Comparator&lt;T&gt; { int compare(T o1, T o2); boolean equals(Object obj); }</code></pre><p>注意最后这个Comparator接口。它里面声明了两个方法，貌似不符合函数接口的定义，但它的确是函数接口。这是因为equals方法是Object的，所有的接口都会声明Object的public方法——虽然大多是隐式的。所以，Comparator显式的声明了equals不影响它依然是个函数接口。</p>
<p>你可以用一个λ表达式为一个函数接口赋值：</p>
<pre><code>Runnable r1 = () -&gt; {System.out.println(&quot;Hello Lambda!&quot;);};</code></pre><p>然后再赋值给一个Object：</p>
<pre><code>Object obj = r1;</code></pre><p>但却不能这样干：</p>
<pre><code>Object obj = () -&gt; {System.out.println(&quot;Hello Lambda!&quot;);}; // ERROR! Object is not a functional interface!</code></pre><p>必须显式的转型成一个函数接口才可以：</p>
<pre><code>Object o = (Runnable) () -&gt; { System.out.println(&quot;hi&quot;); }; // correct</code></pre><p>一个λ表达式只有在转型成一个函数接口后才能被当做Object使用。所以下面这句也不能编译：</p>
<pre><code>System.out.println( () -&gt; {} ); //错误! 目标类型不明</code></pre><p>必须先转型:</p>
<pre><code>System.out.println( (Runnable)() -&gt; {} ); // 正确</code></pre><p>假设你自己写了一个函数接口，长的跟Runnable一模一样：</p>
<pre><code>@FunctionalInterface
public interface MyRunnable {
    public void run();
}</code></pre><p>那么</p>
<pre><code>Runnable r1 =    () -&gt; {System.out.println(&quot;Hello Lambda!&quot;);};
MyRunnable2 r2 = () -&gt; {System.out.println(&quot;Hello Lambda!&quot;);};</code></pre><p>都是正确的写法。这说明一个λ表达式可以有多个目标类型（函数接口），只要函数匹配成功即可。<br>但需注意一个λ表达式必须至少有一个目标类型。</p>
<p>JDK预定义了很多函数接口以避免用户重复定义。最典型的是Function：</p>
<pre><code>@FunctionalInterface
public interface Function&lt;T, R&gt; {  
    R apply(T t);
}</code></pre><p>这个接口代表一个函数，接受一个T类型的参数，并返回一个R类型的返回值。   </p>
<p>另一个预定义函数接口叫做Consumer，跟Function的唯一不同是它没有返回值。</p>
<pre><code>@FunctionalInterface
public interface Consumer&lt;T&gt; {
    void accept(T t);
}</code></pre><p>还有一个Predicate，用来判断某项条件是否满足。经常用来进行筛滤操作：</p>
<pre><code>@FunctionalInterface
public interface Predicate&lt;T&gt; {
    boolean test(T t);
}</code></pre><p>综上所述，一个λ表达式其实就是定义了一个匿名方法，只不过这个方法必须符合至少一个函数接口。</p>
<ol start="3">
<li>λ表达式的使用</li>
</ol>
<p>3.1 λ表达式用在何处</p>
<p>λ表达式主要用于替换以前广泛使用的内部匿名类，各种回调，比如事件响应器、传入Thread类的Runnable等。看下面的例子：</p>
<pre><code>Thread oldSchool = new Thread( new Runnable () {
    @Override
    public void run() {
        System.out.println(&quot;This is from an anonymous class.&quot;);
    }
} );

Thread gaoDuanDaQiShangDangCi = new Thread( () -&gt; {
    System.out.println(&quot;This is from an anonymous method (lambda exp).&quot;);
} );</code></pre><p>注意第二个线程里的λ表达式，你并不需要显式地把它转成一个Runnable，因为Java能根据上下文自动推断出来：一个Thread的构造函数接受一个Runnable参数，而传入的λ表达式正好符合其run()函数，所以Java编译器推断它为Runnable。</p>
<p>从形式上看，λ表达式只是为你节省了几行代码。但将λ表达式引入Java的动机并不仅仅为此。Java8有一个短期目标和一个长期目标。短期目标是：配合“集合类批处理操作”的内部迭代和并行处理（下面将要讲到）；长期目标是将Java向函数式编程语言这个方向引导（并不是要完全变成一门函数式编程语言，只是让它有更多的函数式编程语言的特性），也正是由于这个原因，Oracle并没有简单地使用内部类去实现λ表达式，而是使用了一种更动态、更灵活、易于将来扩展和改变的策略（invokedynamic）。</p>
<p>3.2 λ表达式与集合类批处理操作（或者叫块操作）</p>
<p>上文提到了集合类的批处理操作。这是Java8的另一个重要特性，它与λ表达式的配合使用乃是Java8的最主要特性。集合类的批处理操作API的目的是实现集合类的“内部迭代”，并期望充分利用现代多核CPU进行并行计算。<br>Java8之前集合类的迭代（Iteration）都是外部的，即客户代码。而内部迭代意味着改由Java类库来进行迭代，而不是客户代码。例如：</p>
<pre><code>for(Object o: list) { // 外部迭代
    System.out.println(o);
}</code></pre><p>可以写成：</p>
<pre><code>list.forEach(o -&gt; {System.out.println(o);}); //forEach函数实现内部迭代</code></pre><p>集合类（包括List）现在都有一个forEach方法，对元素进行迭代（遍历），所以我们不需要再写for循环了。forEach方法接受一个函数接口Consumer做参数，所以可以使用λ表达式。</p>
<p>这种内部迭代方法广泛存在于各种语言，如C++的STL算法库、python、ruby、scala等。</p>
<p>Java8为集合类引入了另一个重要概念：流（stream）。一个流通常以一个集合类实例为其数据源，然后在其上定义各种操作。流的API设计使用了管道（pipelines）模式。对流的一次操作会返回另一个流。如同IO的API或者StringBuffer的append方法那样，从而多个不同的操作可以在一个语句里串起来。看下面的例子：</p>
<pre><code>List&lt;Shape&gt; shapes = ...
shapes.stream()
  .filter(s -&gt; s.getColor() == BLUE)
  .forEach(s -&gt; s.setColor(RED));</code></pre><p>首先调用stream方法，以集合类对象shapes里面的元素为数据源，生成一个流。然后在这个流上调用filter方法，挑出蓝色的，返回另一个流。最后调用forEach方法将这些蓝色的物体喷成红色。（forEach方法不再返回流，而是一个终端方法，类似于StringBuffer在调用若干append之后的那个toString）</p>
<p>filter方法的参数是Predicate类型，forEach方法的参数是Consumer类型，它们都是函数接口，所以可以使用λ表达式。</p>
<p>还有一个方法叫parallelStream()，顾名思义它和stream()一样，只不过指明要并行处理，以期充分利用现代CPU的多核特性。</p>
<pre><code>shapes.parallelStream(); // 或shapes.stream().parallel()</code></pre><p>来看更多的例子。下面是典型的大数据处理方法，Filter-Map-Reduce：</p>
<pre><code>//给出一个String类型的数组，找出其中所有不重复的素数
public void distinctPrimary(String... numbers) {
    List&lt;String&gt; l = Arrays.asList(numbers);
    List&lt;Integer&gt; r = l.stream()
            .map(e -&gt; new Integer(e))
            .filter(e -&gt; Primes.isPrime(e))
            .distinct()
            .collect(Collectors.toList());
    System.out.println(&quot;distinctPrimary result is: &quot; + r);
}</code></pre><p>第一步：传入一系列String（假设都是合法的数字），转成一个List，然后调用stream()方法生成流。</p>
<p>第二步：调用流的map方法把每个元素由String转成Integer，得到一个新的流。map方法接受一个Function类型的参数，上面介绍了，Function是个函数接口，所以这里用λ表达式。</p>
<p>第三步：调用流的filter方法，过滤那些不是素数的数字，并得到一个新流。filter方法接受一个Predicate类型的参数，上面介绍了，Predicate是个函数接口，所以这里用λ表达式。</p>
<p>第四步：调用流的distinct方法，去掉重复，并得到一个新流。这本质上是另一个filter操作。</p>
<p>第五步：用collect方法将最终结果收集到一个List里面去。collect方法接受一个Collector类型的参数，这个参数指明如何收集最终结果。在这个例子中，结果简单地收集到一个List中。我们也可以用Collectors.toMap(e-&gt;e, e-&gt;e)把结果收集到一个Map中，它的意思是：把结果收到一个Map，用这些素数自身既作为键又作为值。toMap方法接受两个Function类型的参数，分别用以生成键和值，Function是个函数接口，所以这里都用λ表达式。</p>
<p>你可能会觉得在这个例子里，List l被迭代了好多次，map，filter，distinct都分别是一次循环，效率会不好。实际并非如此。这些返回另一个Stream的方法都是“懒（lazy）”的，而最后返回最终结果的collect方法则是“急（eager）”的。在遇到eager方法之前，lazy的方法不会执行。</p>
<p>当遇到eager方法时，前面的lazy方法才会被依次执行。而且是管道贯通式执行。这意味着每一个元素依次通过这些管道。例如有个元素“3”，首先它被map成整数型3；然后通过filter，发现是素数，被保留下来；又通过distinct，如果已经有一个3了，那么就直接丢弃，如果还没有则保留。这样，3个操作其实只经过了一次循环。</p>
<p>除collect外其它的eager操作还有forEach，toArray，reduce等。</p>
<p>下面来看一下也许是最常用的收集器方法，groupingBy：</p>
<pre><code>//给出一个String类型的数组，找出其中各个素数，并统计其出现次数
public void primaryOccurrence(String... numbers) {
    List&lt;String&gt; l = Arrays.asList(numbers);
    Map&lt;Integer, Integer&gt; r = l.stream()
        .map(e -&gt; new Integer(e))
        .filter(e -&gt; Primes.isPrime(e))
        .collect( Collectors.groupingBy(p-&gt;p, Collectors.summingInt(p-&gt;1)) );
    System.out.println(&quot;primaryOccurrence result is: &quot; + r);
}</code></pre><p>注意这一行：</p>
<pre><code>Collectors.groupingBy(p-&gt;p, Collectors.summingInt(p-&gt;1))</code></pre><p>它的意思是：把结果收集到一个Map中，用统计到的各个素数自身作为键，其出现次数作为值。</p>
<p>下面是一个reduce的例子：</p>
<pre><code>//给出一个String类型的数组，求其中所有不重复素数的和
public void distinctPrimarySum(String... numbers) {
    List&lt;String&gt; l = Arrays.asList(numbers);
    int sum = l.stream()
        .map(e -&gt; new Integer(e))
        .filter(e -&gt; Primes.isPrime(e))
        .distinct()
        .reduce(0, (x,y) -&gt; x+y); // equivalent to .sum()
    System.out.println(&quot;distinctPrimarySum result is: &quot; + sum);
}</code></pre><p>reduce方法用来产生单一的一个最终结果。<br>流有很多预定义的reduce操作，如sum()，max()，min()等。</p>
<p>再举个现实世界里的栗子比如：</p>
<pre><code>// 统计年龄在25-35岁的男女人数、比例
public void boysAndGirls(List&lt;Person&gt; persons) {
    Map&lt;Integer, Integer&gt; result = persons.parallelStream().filter(p -&gt; p.getAge()&gt;=25 &amp;&amp; p.getAge()&lt;=35).
        collect(
            Collectors.groupingBy(p-&gt;p.getSex(), Collectors.summingInt(p-&gt;1))
    );
    System.out.print(&quot;boysAndGirls result is &quot; + result);
    System.out.println(&quot;, ratio (male : female) is &quot; + (float)result.get(Person.MALE)/result.get(Person.FEMALE));
}</code></pre><p>3.3 λ表达式的更多用法</p>
<pre><code>// 嵌套的λ表达式
Callable&lt;Runnable&gt; c1 = () -&gt; () -&gt; { System.out.println(&quot;Nested lambda&quot;); };
c1.call().run();

// 用在条件表达式中
Callable&lt;Integer&gt; c2 = true ? (() -&gt; 42) : (() -&gt; 24);
System.out.println(c2.call());

// 定义一个递归函数，注意须用this限定
protected UnaryOperator&lt;Integer&gt; factorial = i -&gt; i == 0 ? 1 : i * this.factorial.apply( i - 1 );
...
System.out.println(factorial.apply(3));</code></pre><p>在Java中，随声明随调用的方式是不行的，比如下面这样，声明了一个λ表达式(x, y) -&gt; x + y，同时企图通过传入实参(2, 3)来调用它：</p>
<pre><code>int five = ( (x, y) -&gt; x + y ) (2, 3); // ERROR! try to call a lambda in-place</code></pre><p>这在C++中是可以的，但Java中不行。Java的λ表达式只能用作赋值、传参、返回值等。</p>
<ol start="4">
<li>其它相关概念</li>
</ol>
<p>4.1 捕获（Capture）</p>
<p>捕获的概念在于解决在λ表达式中我们可以使用哪些外部变量（即除了它自己的参数和内部定义的本地变量）的问题。</p>
<p>答案是：与内部类非常相似，但有不同点。不同点在于内部类总是持有一个其外部类对象的引用。而λ表达式呢，除非在它内部用到了其外部类（包围类）对象的方法或者成员，否则它就不持有这个对象的引用。</p>
<p>在Java8以前，如果要在内部类访问外部对象的一个本地变量，那么这个变量必须声明为final才行。在Java8中，这种限制被去掉了，代之以一个新的概念，“effectively final”。它的意思是你可以声明为final，也可以不声明final但是按照final来用，也就是一次赋值永不改变。换句话说，保证它加上final前缀后不会出编译错误。</p>
<p>在Java8中，内部类和λ表达式都可以访问effectively final的本地变量。λ表达式的例子如下：</p>
<pre><code>...    
int tmp1 = 1; //包围类的成员变量
static int tmp2 = 2; //包围类的静态成员变量
public void testCapture() {
    int tmp3 = 3; //没有声明为final，但是effectively final的本地变量
    final int tmp4 = 4; //声明为final的本地变量
    int tmp5 = 5; //普通本地变量

    Function&lt;Integer, Integer&gt; f1 = i -&gt; i + tmp1;
    Function&lt;Integer, Integer&gt; f2 = i -&gt; i + tmp2;
    Function&lt;Integer, Integer&gt; f3 = i -&gt; i + tmp3;
    Function&lt;Integer, Integer&gt; f4 = i -&gt; i + tmp4;
    Function&lt;Integer, Integer&gt; f5 = i -&gt; {
        tmp5  += i; // 编译错！对tmp5赋值导致它不是effectively final的
        return tmp5;
    };
    ...
    tmp5 = 9; // 编译错！对tmp5赋值导致它不是effectively final的
}
...</code></pre><p>Java要求本地变量final或者effectively final的原因是多线程并发问题。内部类、λ表达式都有可能在不同的线程中执行，允许多个线程同时修改一个本地变量不符合Java的设计理念。</p>
<p>4.2 方法引用（Method reference）</p>
<p>任何一个λ表达式都可以代表某个函数接口的唯一方法的匿名描述符。我们也可以使用某个类的某个具体方法来代表这个描述符，叫做方法引用。例如：</p>
<pre><code>Integer::parseInt //静态方法引用
System.out::print //实例方法引用
Person::new       //构造器引用</code></pre><p>下面是一组例子，教你使用方法引用代替λ表达式：</p>
<pre><code>//c1 与 c2 是一样的（静态方法引用）
Comparator&lt;Integer&gt; c2 = (x, y) -&gt; Integer.compare(x, y);
Comparator&lt;Integer&gt; c1 = Integer::compare;

//下面两句是一样的（实例方法引用1）
persons.forEach(e -&gt; System.out.println(e));
persons.forEach(System.out::println);

//下面两句是一样的（实例方法引用2）
persons.forEach(person -&gt; person.eat());
persons.forEach(Person::eat);

//下面两句是一样的（构造器引用）
strList.stream().map(s -&gt; new Integer(s));
strList.stream().map(Integer::new);</code></pre><p>使用方法引用，你的程序会变得更短些。现在distinctPrimarySum方法可以改写如下：</p>
<pre><code>public void distinctPrimarySum(String... numbers) {
    List&lt;String&gt; l = Arrays.asList(numbers);
    int sum = l.stream().map(Integer::new).filter(Primes::isPrime).distinct().sum();
    System.out.println(&quot;distinctPrimarySum result is: &quot; + sum);
}</code></pre><p>还有一些其它的方法引用:</p>
<pre><code>super::toString //引用某个对象的父类方法
String[]::new //引用一个数组的构造器</code></pre><p>4.3 默认方法（Default method）</p>
<p>Java8中，接口声明里可以有方法实现了，叫做默认方法。在此之前，接口里的方法全部是抽象方法。</p>
<pre><code>public interface MyInterf {

    String m1();

    default String m2() {
        return &quot;Hello default method!&quot;;
    }

}</code></pre><p>这实际上混淆了接口和抽象类，但一个类仍然可以实现多个接口，而只能继承一个抽象类。</p>
<p>这么做的原因是：由于Collection库需要为批处理操作添加新的方法，如forEach()，stream()等，但是不能修改现有的Collection接口——如果那样做的话所有的实现类都要进行修改，包括很多客户自制的实现类。所以只好使用这种妥协的办法。</p>
<p>如此一来，我们就面临一种类似多继承的问题。如果类Sub继承了两个接口，Base1和Base2，而这两个接口恰好具有完全相同的两个默认方法，那么就会产生冲突。这时Sub类就必须通过重载来显式指明自己要使用哪一个接口的实现（或者提供自己的实现）：</p>
<pre><code>public class Sub implements Base1, Base2 {

    public void hello() {
        Base1.super.hello(); //使用Base1的实现
    }

}</code></pre><p>除了默认方法，Java8的接口也可以有静态方法的实现：</p>
<pre><code>public interface MyInterf {

    String m1();

    default String m2() {
        return &quot;Hello default method!&quot;;
    }

    static String m3() {
        return &quot;Hello static method in Interface!&quot;;
    }

}</code></pre><p>4.4 生成器函数（Generator function）</p>
<p>有时候一个流的数据源不一定是一个已存在的集合对象，也可能是个“生成器函数”。一个生成器函数会产生一系列元素，供给一个流。Stream.generate(Supplier<t> s)就是一个生成器函数。其中参数Supplier是一个函数接口，里面有唯一的抽象方法 <t> get()。</t></t></p>
<p>下面这个例子生成并打印5个随机数：</p>
<pre><code>Stream.generate(Math::random).limit(5).forEach(System.out::println);</code></pre><p>注意这个limit(5)，如果没有这个调用，那么这条语句会永远地执行下去。也就是说这个生成器是无穷的。这种调用叫做终结操作，或者短路（short-circuiting）操作。</p>

    </div>
</article>


                </div>
                <aside class="col-md-4 gal-left" id="sidebar">
    <!-- 此为sidebar的搜索框, 非搜索结果页面 -->
<aside id="sidebar-search">
    <div class="search hidden-xs" data-aos="fade-up" data-aos-duration="2000">
        <form class="form-inline clearfix" id="search-form" method="get"
              action="/search/index.html">
            <input type="text" name="s" class="form-control" id="searchInput" placeholder="搜索文章~" autocomplete="off">
            <button class="btn btn-danger btn-gal" type="submit">
                <i class="fa fa-search"></i>
            </button>
        </form>
    </div>
</aside>
    <aside id="sidebar-author">
    <div class="panel panel-gal" data-aos="flip-right" data-aos-duration="3000">
        <div class="panel-heading" style="text-align: center">
            <i class="fa fa-quote-left"></i>
            挥手の骑士
            <i class="fa fa-quote-right"></i>
        </div>
        <div class="author-panel text-center">
            <img src="/imgs/avatar.jpg" width="140" height="140"
                 alt="个人头像" class="author-image">
            <p class="author-description"></p>
        </div>
    </div>
</aside>
    
    <!-- 要配置好leancloud才能开启此小工具 -->
    
    
    <aside id="sidebar-recent_posts">
    <div class="panel panel-gal recent hidden-xs" data-aos="fade-up" data-aos-duration="2000">
        <div class="panel-heading">
            <i class="fa fa-refresh"></i>
            近期文章
            <i class="fa fa-times-circle panel-remove"></i>
            <i class="fa fa-chevron-circle-up panel-toggle"></i>
        </div>
        <ul class="list-group list-group-flush">
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2020/08/18/55226946/">agent v3.2.1 插件激活码</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2020/07/22/2412014068/">干掉 CMS 未来属于 ZGC</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2020/07/21/4075219928/">OpenJDK 和 OracleJDK 的抉择</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2020/07/20/368594680/">Flutter 音乐播放器开发之路（一）</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2020/07/15/3650854255/">XaaS 到底是什么？</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2020/07/15/4036766117/">让 Windows 10 开机自动登录</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2020/07/14/466733536/">给 Flutter 项目初始化构建提个速</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2020/06/12/4125727764/">JavaScript 中 Splice 函数与数组塌陷</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2020/06/12/1586694155/">在 Java 中缩写 Lambda 表达式</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2020/06/04/2276828982/">博客文章规范</a>
                </span>
            </li>
            
        </ul>
    </div>
</aside>
    
    
    <aside id="sidebar-rand_posts">
    <div class="panel panel-gal recent hidden-xs" data-aos="fade-up" data-aos-duration="2000">
        <div class="panel-heading">
            <i class="fa fa-refresh"></i>
            随机文章
            <i class="fa fa-times-circle panel-remove"></i>
            <i class="fa fa-chevron-circle-up panel-toggle"></i>
        </div>
        <ul class="list-group list-group-flush">
            
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2019/01/09/2697897267/">Java 8 中的模板字符串</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2017/11/10/3226173500/">Java 中的占位符</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2018/09/07/1615584056/">Java 配置类与 XML</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2018/04/24/2553779906/">PL/SQL Developer 连接远程数据库</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2020/07/15/3650854255/">XaaS 到底是什么？</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2017/09/27/3303306385/">关于 Mdui 主题中的一些设置问题</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2019/04/24/3043579305/">合理设计条件分支</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2018/05/24/1593155822/">永远不一致的分布式系统</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2019/01/25/2610927596/">用 Lombok 来简化代码</a>
                </span>
            </li>
            
            <li class="list-group-item">
                <span class="post-title">
                    <a href="/2017/12/22/2700866660/">解决 IDEA 打包输出缺少 mybatis 配置的 SQL 文件问题</a>
                </span>
            </li>
            
        </ul>
    </div>
</aside>
    
    
    <aside id="gal-sets">
        <div class="panel panel-gal hidden-xs" data-aos="fade-up" data-aos-duration="2000">
            <ul class="nav nav-pills pills-gal">

                
                <li>
                    <a href="/2018/09/03/2921176476/index.html#sidebar-tags" data-toggle="tab" id="tags-tab">热门标签</a>
                </li>
                
                
                <li>
                    <a href="/2018/09/03/2921176476/index.html#sidebar-friend-links" data-toggle="tab" id="friend-links-tab">友情链接</a>
                </li>
                
                
                <li>
                    <a href="/2018/09/03/2921176476/index.html#sidebar-links" data-toggle="tab" id="links-tab">个人链接</a>
                </li>
                
            </ul>
            <div class="tab-content">
                
                <div class="cloud-tags tab-pane nav bs-sidenav fade" id="sidebar-tags">
    
    <a href="/tags/SpringSecurity/" style="font-size: 17.707647899268213px;" class="tag-cloud-link">SpringSecurity</a>
    
    <a href="/tags/Xml/" style="font-size: 18.553176353430995px;" class="tag-cloud-link">Xml</a>
    
    <a href="/tags/随笔/" style="font-size: 8.406387067785314px;" class="tag-cloud-link">随笔</a>
    
    <a href="/tags/大前端/" style="font-size: 9.684597193117748px;" class="tag-cloud-link">大前端</a>
    
    <a href="/tags/Dart/" style="font-size: 12.298484479488586px;" class="tag-cloud-link">Dart</a>
    
    <a href="/tags/Flutter/" style="font-size: 19.174868945878394px;" class="tag-cloud-link">Flutter</a>
    
    <a href="/tags/Go/" style="font-size: 9.149375453553908px;" class="tag-cloud-link">Go</a>
    
    <a href="/tags/IDEA/" style="font-size: 14.104270634337801px;" class="tag-cloud-link">IDEA</a>
    
    <a href="/tags/Java/" style="font-size: 17.970386464492236px;" class="tag-cloud-link">Java</a>
    
    <a href="/tags/JavaScript/" style="font-size: 9.392325821649726px;" class="tag-cloud-link">JavaScript</a>
    
    <a href="/tags/Mysql/" style="font-size: 14.349063834808026px;" class="tag-cloud-link">Mysql</a>
    
    <a href="/tags/Oracle/" style="font-size: 9.527165023618426px;" class="tag-cloud-link">Oracle</a>
    
    <a href="/tags/Photoshop/" style="font-size: 12.783577095256723px;" class="tag-cloud-link">Photoshop</a>
    
    <a href="/tags/RPG-Maker-MV/" style="font-size: 11.527924615101401px;" class="tag-cloud-link">RPG Maker MV</a>
    
    <a href="/tags/Restful/" style="font-size: 17.01046551836756px;" class="tag-cloud-link">Restful</a>
    
    <a href="/tags/Redis/" style="font-size: 19.708622619691482px;" class="tag-cloud-link">Redis</a>
    
    <a href="/tags/SQL/" style="font-size: 17.167168109011815px;" class="tag-cloud-link">SQL</a>
    
    <a href="/tags/SpringBoot/" style="font-size: 15.321521735250592px;" class="tag-cloud-link">SpringBoot</a>
    
    <a href="/tags/Swagger/" style="font-size: 19.28575661435412px;" class="tag-cloud-link">Swagger</a>
    
    <a href="/tags/Windows/" style="font-size: 13.881905653715231px;" class="tag-cloud-link">Windows</a>
    
    <a href="/tags/Deepleanring/" style="font-size: 13.421687115369728px;" class="tag-cloud-link">Deepleanring</a>
    
    <a href="/tags/Vue/" style="font-size: 10.201088131309767px;" class="tag-cloud-link">Vue</a>
    
    <a href="/tags/Docker/" style="font-size: 16.1245855394188px;" class="tag-cloud-link">Docker</a>
    
    <a href="/tags/Hexo/" style="font-size: 15.665104091909605px;" class="tag-cloud-link">Hexo</a>
    
    <a href="/tags/VsCode/" style="font-size: 13.556716637868425px;" class="tag-cloud-link">VsCode</a>
    
    <a href="/tags/SpringCloud/" style="font-size: 17.640517372511063px;" class="tag-cloud-link">SpringCloud</a>
    
</div>
                
                
                <div class="friend-links tab-pane nav bs-sidenav fade" id="sidebar-friend-links">
    
    <li>
        <a href="http://noodlefighter.com" target="_blank">虾包</a>
    </li>
    
    <li>
        <a href="https://loli.la" target="_blank">超级叫了</a>
    </li>
    
    <li>
        <a href="https://konata9.github.io" target="_blank">吃土的小此方</a>
    </li>
    
</div>
                
                
                <div class="links tab-pane nav bs-sidenav fade" id="sidebar-links">
    
    <li>
        <a href="https://github.com/yihuishou" target="_blank">Github</a>
    </li>
    
    <li>
        <a href="https://gite.com/yihuishou" target="_blank">码云</a>
    </li>
    
    <li>
        <a href="https://www.zhihu.com/people/hei-yi-tian-shi-48" target="_blank">知乎</a>
    </li>
    
</div>
                
            </div>
        </div>
    </aside>
    
</aside>
            </div>
        </div>
    </div>
    <footer id="gal-footer">
    <div class="container">
        Copyright © 2018 挥手の骑士 Powered by <a href="https://hexo.io/" target="_blank">Hexo</a>.&nbsp;Theme by <a href="https://github.com/ZEROKISEKI" target="_blank">AONOSORA</a>
    </div>
</footer>

<!-- 回到顶端 -->
<div id="gal-gotop">
    <i class="fa fa-angle-up"></i>
</div>
</body>
<script src="/js/activate-power-mode.js"></script>
<script>

    // 配置highslide
	hs.graphicsDir = '/js/highslide/graphics/'
    hs.outlineType = "rounded-white";
    hs.dimmingOpacity = 0.8;
    hs.outlineWhileAnimating = true;
    hs.showCredits = false;
    hs.captionEval = "this.thumb.alt";
    hs.numberPosition = "caption";
    hs.align = "center";
    hs.transitions = ["expand", "crossfade"];
    hs.lang.number = '共%2张图, 当前是第%1张';
    hs.addSlideshow({
      interval: 5000,
      repeat: true,
      useControls: true,
      fixedControls: "fit",
      overlayOptions: {
        opacity: 0.75,
        position: "bottom center",
        hideOnMouseOut: true
      }
    })

    // 初始化aos
    AOS.init({
      duration: 1000,
      delay: 0,
      easing: 'ease-out-back'
    });

</script>
<script>
	POWERMODE.colorful = 'true';    // make power mode colorful
	POWERMODE.shake = 'true';       // turn off shake
	// TODO 这里根据具体情况修改
	document.body.addEventListener('input', POWERMODE);
</script>
<script>
    window.slideConfig = {
      prefix: '/imgs/slide/background',
      ext: 'jpg',
      maxCount: '6'
    }
</script>
<script src="/js/hs.js"></script>
<script src="/js/blog.js"></script>



</html>